當系統內的物件們各自溝通的情況日益嚴重時,建立一個負責溝通的集中所,讓元件們不在「直接」,而是「間接」與他者溝通。
通常在程式剛開發的時候,為了快速達到開發目的,可能會出現物件們直接與其他物件溝通,像是:
隨著物件的數量增長,交互溝通的情況宛如人際連結的網絡般,越來越複雜,這在開發上絕對不是一件好事,日後如果有修改的需求,很容易變成上 Patch 的情況。
因此,可以建立一個負責溝通的物件,所有物件都要通過該物件才能完成溝通的工作,至少讓複雜、混亂的溝通關係簡化成星狀圖,
作法是:
以下範例以「自行車隊的行動」為核心製作。
系統內的物件親代:Player
public interface Player {
void setMediator(Mediator mediator);
String getName();
String getJob();
}
負責溝通的物件親代:Mediator
public interface Mediator {
void registerPlayer(Player player);
void changeFormation(int type);
void accelerate();
void decelerate();
void notifyTeamMembers(String message, Player requester);
}
系統內的物件子代:Sprinter
、Domestique
、TimeTrialist
、ClimbingSpecialist
public class Sprinter implements Player {
private String name;
private String job = "Sprinter";
private Mediator mediator;
public Sprinter(String name) {
this.name = name;
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public String getName() {
return name;
}
@Override
public String getJob() {
return job;
}
public void teamChangeFormation(int type) {
mediator.changeFormation(type);
}
public void changeFormation(int position) {
System.out.println(job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
public void teamAccelerate() {
mediator.accelerate();
}
public void personalAccelerate() {
System.out.println("車手 " + name + " 收到通知,開始加速");
}
public void teamDecelerate() {
mediator.decelerate();
}
public void personalDecelerate() {
System.out.println("車手 " + name + " 收到通知,開始減速");
}
public void notifyTeamMembers(String message) {
mediator.notifyTeamMembers(message, this);
}
public void receiveMessage(String message) {
System.out.println("車手 " + name + " 收到: " + message);
}
}
public class Domestique implements Player {
private String name;
private String job = "Domestique";
private Mediator mediator;
public Domestique(String name) {
this.name = name;
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public String getName() {
return name;
}
@Override
public String getJob() {
return job;
}
public void teamChangeFormation(int type) {
mediator.changeFormation(type);
}
public void changeFormation(int position) {
System.out.println(job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
public void teamAccelerate() {
mediator.accelerate();
}
public void personalAccelerate() {
System.out.println("車手 " + name + " 收到通知,開始加速");
}
public void teamDecelerate() {
mediator.decelerate();
}
public void personalDecelerate() {
System.out.println("車手 " + name + " 收到通知,開始減速");
}
public void notifyTeamMembers(String message) {
mediator.notifyTeamMembers(message, this);
}
public void receiveMessage(String message) {
System.out.println("車手 " + name + " 收到: " + message);
}
}
public class TimeTrialist implements Player {
private String name;
private String job = "TimeTrialist";
private Mediator mediator;
public TimeTrialist(String name) {
this.name = name;
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public String getName() {
return name;
}
@Override
public String getJob() {
return job;
}
public void teamChangeFormation(int type) {
mediator.changeFormation(type);
}
public void changeFormation(int position) {
System.out.println(job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
public void teamAccelerate() {
mediator.accelerate();
}
public void personalAccelerate() {
System.out.println("車手 " + name + " 收到通知,開始加速");
}
public void teamDecelerate() {
mediator.decelerate();
}
public void personalDecelerate() {
System.out.println("車手 " + name + " 收到通知,開始減速");
}
public void notifyTeamMembers(String message) {
mediator.notifyTeamMembers(message, this);
}
public void receiveMessage(String message) {
System.out.println("車手 " + name + " 收到: " + message);
}
}
public class ClimbingSpecialist implements Player {
private String name;
private String job = "ClimbingSpecialist";
private Mediator mediator;
public ClimbingSpecialist(String name) {
this.name = name;
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public String getName() {
return name;
}
@Override
public String getJob() {
return job;
}
public void teamChangeFormation(int type) {
mediator.changeFormation(type);
}
public void changeFormation(int position) {
System.out.println(job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
public void teamAccelerate() {
mediator.accelerate();
}
public void personalAccelerate() {
System.out.println("車手 " + name + " 收到通知,開始加速");
}
public void teamDecelerate() {
mediator.decelerate();
}
public void personalDecelerate() {
System.out.println("車手 " + name + " 收到通知,開始減速");
}
public void notifyTeamMembers(String message) {
mediator.notifyTeamMembers(message, this);
}
public void receiveMessage(String message) {
System.out.println("車手 " + name + " 收到: " + message);
}
}
負責溝通的物件子代:PitStop
(Mediator 物件)
public class PitStop implements Mediator {
private Sprinter sprinter;
private Domestique domestique;
private TimeTrialist timeTrialist;
private ClimbingSpecialist climbingSpecialist;
@Override
public void registerPlayer(Player player) {
player.setMediator(this);
switch (player.getJob()) {
case "Sprinter":
sprinter = (Sprinter) player;
break;
case "Domestique":
domestique = (Domestique) player;
break;
case "TimeTrialist":
timeTrialist = (TimeTrialist) player;
break;
case "ClimbingSpecialist":
climbingSpecialist = (ClimbingSpecialist) player;
break;
}
}
@Override
public void changeFormation(int type) {
switch (type) {
case 1:
sprinter.changeFormation(1);
domestique.changeFormation(2);
timeTrialist.changeFormation(3);
climbingSpecialist.changeFormation(4);
break;
case 2:
domestique.changeFormation(1);
timeTrialist.changeFormation(2);
climbingSpecialist.changeFormation(3);
sprinter.changeFormation(4);
break;
case 3:
climbingSpecialist.changeFormation(1);
domestique.changeFormation(2);
sprinter.changeFormation(3);
timeTrialist.changeFormation(4);
break;
case 4:
timeTrialist.changeFormation(1);
climbingSpecialist.changeFormation(2);
sprinter.changeFormation(3);
domestique.changeFormation(4);
break;
}
System.out.println("");
}
@Override
public void accelerate() {
sprinter.personalAccelerate();
domestique.personalAccelerate();
timeTrialist.personalAccelerate();
climbingSpecialist.personalAccelerate();
System.out.println("");
}
@Override
public void decelerate() {
sprinter.personalDecelerate();
domestique.personalDecelerate();
timeTrialist.personalDecelerate();
climbingSpecialist.personalDecelerate();
System.out.println("");
}
@Override
public void notifyTeamMembers(String message, Player requester) {
String finalMessage = "車手: " + requester.getName() + " 通知: " + message;
switch (requester.getJob()) {
case "Sprinter":
domestique.receiveMessage(finalMessage);
timeTrialist.receiveMessage(finalMessage);
climbingSpecialist.receiveMessage(finalMessage);
break;
case "Domestique":
sprinter.receiveMessage(finalMessage);
timeTrialist.receiveMessage(finalMessage);
climbingSpecialist.receiveMessage(finalMessage);
break;
case "TimeTrialist":
sprinter.receiveMessage(finalMessage);
domestique.receiveMessage(finalMessage);
climbingSpecialist.receiveMessage(finalMessage);
break;
case "ClimbingSpecialist":
sprinter.receiveMessage(finalMessage);
domestique.receiveMessage(finalMessage);
timeTrialist.receiveMessage(finalMessage);
break;
}
System.out.println("");
}
}
測試,建立車隊成員後,模擬溝通情境:BicycleRacingMediatorSample
public class BicycleRacingMediatorSample {
public static void main(String[] args) {
Mediator mediator = new PitStop();
Sprinter sprinter = new Sprinter("Roger");
Domestique domestique = new Domestique("Victor");
TimeTrialist timeTrialist = new TimeTrialist("David");
ClimbingSpecialist climbingSpecialist = new ClimbingSpecialist("Eric");
/** 註冊隊友 */
mediator.registerPlayer(sprinter);
mediator.registerPlayer(domestique);
mediator.registerPlayer(timeTrialist);
mediator.registerPlayer(climbingSpecialist);
/** 要求加速 */
sprinter.teamAccelerate();
domestique.teamAccelerate();
/** 要求減速 */
timeTrialist.teamDecelerate();
climbingSpecialist.teamChangeFormation(2);
/** 通知隊友 */
domestique.notifyTeamMembers("前方有大彎道,向右");
/** 最後衝刺 */
sprinter.teamChangeFormation(1);
sprinter.notifyTeamMembers("抵達終點線");
}
}
系統內的物件親代:Player
/** @interface */
class Player {
/** @param {Mediator} mediator */
setMediator(mediator) { return };
getName() { return ""; }
getJob() { return ""; }
}
負責溝通的物件親代:Mediator
/** @interface */
class Mediator {
/** @param {Player} player */
registerPlayer(player) { return; }
/** @param {number} type */
changeFormation(type) { return; }
accelerate() { return; }
decelerate() { return; }
/**
* @param {string} message
* @param {Player} requester
*/
notifyTeamMembers(message, requester) { return; }
}
系統內的物件子代:Sprinter
、Domestique
、TimeTrialist
、ClimbingSpecialist
class Sprinter extends Player {
constructor(name) {
super();
/** @type {string} */
this.name = name;
this.job = "Sprinter";
/** @type {Mediator} */
this.mediator = null;
}
/**
* @override
* @param {Mediator} mediator
*/
setMediator(mediator) {
this.mediator = mediator;
}
/** @override */
getName() {
return this.name;
}
/** @override */
getJob() {
return this.job;
}
/** @param {number} type */
teamChangeFormation(type) {
this.mediator.changeFormation(type);
}
/** @param {number} position */
changeFormation(position) {
console.log(this.job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
teamAccelerate() {
this.mediator.accelerate();
}
personalAccelerate() {
console.log("車手 " + this.name + " 收到通知,開始加速");
}
teamDecelerate() {
this.mediator.decelerate();
}
personalDecelerate() {
console.log("車手 " + this.name + " 收到通知,開始減速");
}
/** @param {string} message */
notifyTeamMembers(message) {
this.mediator.notifyTeamMembers(message, this);
}
/** @param {string} message */
receiveMessage(message) {
console.log("車手 " + this.name + " 收到: " + message);
}
}
class Domestique extends Player {
constructor(name) {
super();
/** @type {string} */
this.name = name;
this.job = "Domestique";
/** @type {Mediator} */
this.mediator = null;
}
/**
* @override
* @param {Mediator} mediator
*/
setMediator(mediator) {
this.mediator = mediator;
}
/** @override */
getName() {
return this.name;
}
/** @override */
getJob() {
return this.job;
}
/** @param {number} type */
teamChangeFormation(type) {
this.mediator.changeFormation(type);
}
/** @param {number} position */
changeFormation(position) {
console.log(this.job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
teamAccelerate() {
this.mediator.accelerate();
}
personalAccelerate() {
console.log("車手 " + this.name + " 收到通知,開始加速");
}
teamDecelerate() {
this.mediator.decelerate();
}
personalDecelerate() {
console.log("車手 " + this.name + " 收到通知,開始減速");
}
/** @param {string} message */
notifyTeamMembers(message) {
this.mediator.notifyTeamMembers(message, this);
}
/** @param {string} message */
receiveMessage(message) {
console.log("車手 " + this.name + " 收到: " + message);
}
}
class TimeTrialist extends Player {
constructor(name) {
super();
/** @type {string} */
this.name = name;
this.job = "TimeTrialist";
/** @type {Mediator} */
this.mediator = null;
}
/**
* @override
* @param {Mediator} mediator
*/
setMediator(mediator) {
this.mediator = mediator;
}
/** @override */
getName() {
return this.name;
}
/** @override */
getJob() {
return this.job;
}
/** @param {number} type */
teamChangeFormation(type) {
this.mediator.changeFormation(type);
}
/** @param {number} position */
changeFormation(position) {
console.log(this.job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
teamAccelerate() {
this.mediator.accelerate();
}
personalAccelerate() {
console.log("車手 " + this.name + " 收到通知,開始加速");
}
teamDecelerate() {
this.mediator.decelerate();
}
personalDecelerate() {
console.log("車手 " + this.name + " 收到通知,開始減速");
}
/** @param {string} message */
notifyTeamMembers(message) {
this.mediator.notifyTeamMembers(message, this);
}
/** @param {string} message */
receiveMessage(message) {
console.log("車手 " + this.name + " 收到: " + message);
}
}
class ClimbingSpecialist extends Player {
constructor(name) {
super();
/** @type {string} */
this.name = name;
this.job = "ClimbingSpecialist";
/** @type {Mediator} */
this.mediator = null;
}
/**
* @override
* @param {Mediator} mediator
*/
setMediator(mediator) {
this.mediator = mediator;
}
/** @override */
getName() {
return this.name;
}
/** @override */
getJob() {
return this.job;
}
/** @param {number} type */
teamChangeFormation(type) {
this.mediator.changeFormation(type);
}
/** @param {number} position */
changeFormation(position) {
console.log(this.job + " 收到通知,移動到車隊的第 " + position + " 個位置");
}
teamAccelerate() {
this.mediator.accelerate();
}
personalAccelerate() {
console.log("車手 " + this.name + " 收到通知,開始加速");
}
teamDecelerate() {
this.mediator.decelerate();
}
personalDecelerate() {
console.log("車手 " + this.name + " 收到通知,開始減速");
}
/** @param {string} message */
notifyTeamMembers(message) {
this.mediator.notifyTeamMembers(message, this);
}
/** @param {string} message */
receiveMessage(message) {
console.log("車手 " + this.name + " 收到: " + message);
}
}
負責溝通的物件子代:PitStop
(Mediator 物件)
class PitStop extends Mediator {
constructor() {
super();
/** @type {Sprinter} */
this.sprinter = null;
/** @type {Domestique} */
this.domestique = null;
/** @type {TimeTrialist} */
this.timeTrialist = null;
/** @type {ClimbingSpecialist} */
this.climbingSpecialist = null;
}
/**
* @override
* @param {Player} player
*/
registerPlayer(player) {
player.setMediator(this);
switch (player.getJob()) {
case "Sprinter":
this.sprinter = player;
break;
case "Domestique":
this.domestique = player;
break;
case "TimeTrialist":
this.timeTrialist = player;
break;
case "ClimbingSpecialist":
this.climbingSpecialist = player;
break;
}
}
/**
* @override
* @param {number} type
*/
changeFormation(type) {
switch (type) {
case 1:
this.sprinter.changeFormation(1);
this.domestique.changeFormation(2);
this.timeTrialist.changeFormation(3);
this.climbingSpecialist.changeFormation(4);
break;
case 2:
this.domestique.changeFormation(1);
this.timeTrialist.changeFormation(2);
this.climbingSpecialist.changeFormation(3);
this.sprinter.changeFormation(4);
break;
case 3:
this.climbingSpecialist.changeFormation(1);
this.domestique.changeFormation(2);
this.sprinter.changeFormation(3);
this.timeTrialist.changeFormation(4);
break;
case 4:
this.timeTrialist.changeFormation(1);
this.climbingSpecialist.changeFormation(2);
this.sprinter.changeFormation(3);
this.domestique.changeFormation(4);
break;
}
console.log("");
}
/** @override */
accelerate() {
this.sprinter.personalAccelerate();
this.domestique.personalAccelerate();
this.timeTrialist.personalAccelerate();
this.climbingSpecialist.personalAccelerate();
console.log("");
}
/** @override */
decelerate() {
this.sprinter.personalDecelerate();
this.domestique.personalDecelerate();
this.timeTrialist.personalDecelerate();
this.climbingSpecialist.personalDecelerate();
console.log("");
}
/**
* @override
* @param {string} message
* @param {Player} requester
*/
notifyTeamMembers(message, requester) {
const finalMessage = "車手: " + requester.getName() + " 通知: " + message;
switch (requester.getJob()) {
case "Sprinter":
this.domestique.receiveMessage(finalMessage);
this.timeTrialist.receiveMessage(finalMessage);
this.climbingSpecialist.receiveMessage(finalMessage);
break;
case "Domestique":
this.sprinter.receiveMessage(finalMessage);
this.timeTrialist.receiveMessage(finalMessage);
this.climbingSpecialist.receiveMessage(finalMessage);
break;
case "TimeTrialist":
this.sprinter.receiveMessage(finalMessage);
this.domestique.receiveMessage(finalMessage);
this.climbingSpecialist.receiveMessage(finalMessage);
break;
case "ClimbingSpecialist":
this.sprinter.receiveMessage(finalMessage);
this.domestique.receiveMessage(finalMessage);
this.timeTrialist.receiveMessage(finalMessage);
break;
}
console.log("");
}
}
測試,建立車隊成員後,模擬溝通情境:BicycleRacingMediatorSample
const bicycleRacingMediatorSample = () => {
const mediator = new PitStop();
const sprinter = new Sprinter("Roger");
const domestique = new Domestique("Victor");
const timeTrialist = new TimeTrialist("David");
const climbingSpecialist = new ClimbingSpecialist("Eric");
/** 註冊隊友 */
mediator.registerPlayer(sprinter);
mediator.registerPlayer(domestique);
mediator.registerPlayer(timeTrialist);
mediator.registerPlayer(climbingSpecialist);
/** 要求加速 */
sprinter.teamAccelerate();
domestique.teamAccelerate();
/** 要求減速 */
timeTrialist.teamDecelerate();
climbingSpecialist.teamChangeFormation(2);
/** 通知隊友 */
domestique.notifyTeamMembers("前方有大彎道,向右");
/** 最後衝刺 */
sprinter.teamChangeFormation(1);
sprinter.notifyTeamMembers("抵達終點線");
};
bicycleRacingMediatorSample();
起初,我以為 Mediator 模式跟 React 或是 Redux 的中心式管理 Data 相似,實則完全不同。因為 Mediator 模式的中間人不負責儲存變數,只負責「傳遞」,物件之間互相影響後的狀態如何,中間人都不會知道。
該模式玩味之處還有一點,讓所有物件都依賴中間人,造成該中間人成為超級重要的 God Class,變相增加物件的依賴性,這會在往後成為開發上的障礙。
因此,此模式適合在確定物件之間有著高度依賴性,並且往後的變動不多、不大,那就可以安心使用了。
明天將介紹 Behavioural patterns 的第六個模式:Memento 模式。